// File: jsShellEnv.js
// usage: $js -f jsShellEnv.js - ....
//
// used by the js shell program only
// contains common functions for testing purposes
//

// console is not defined in the js shell but print is
function console() {};
console.log = print;

// A sophisticated version of typeOf
//
var type = (function(global) {
    var cache = {};
    return function(obj) {
        var key;
        return obj === null ? 'null' // null
            : obj === global ? 'WINDOW' // window in browser or global in nodejs
            : (key = typeof obj) !== 'object' ? key // basic: string, boolean, number, undefined, function
            : obj.nodeType ? 'DOMobject' // DOM element
            : cache[key = Object.prototype.toString.call(obj)] // cached. date, regexp, error, object, array, math
            || (cache[key] = key.slice(8, -1).toLowerCase()); // get XXXX from [object XXXX], and cache it
    };
}) (this);

function toggleStopWatch() {
    var baseTime = (new Date()).getTime();
    return function() { return ((new Date()).getTime() - baseTime); }
}

function isArray (ar) {
    return type(ar) == 'array';
}

function convertToArray(ar) {
	return Array.prototype.slice.call(ar);
}

function isRegExp(re) {
    var s;
    try {
      s = '' + re;
    } catch (e) {
        return false;
    }

    return re instanceof RegExp || // easy case
       // duck-type for context-switching evalcx case
       typeof(re) === 'function' &&
       re.constructor.name === 'RegExp' &&
       re.compile &&
       re.test &&
       re.exec &&
       s.match(/^\/.*\/[gim]{0,3}$/);
}

function isDate(d) {
    return type(d) == 'date';;
}

function isUndefinedOrNull (value) {
    return value === null || value === undefined;
}

function isArguments (object) {
    return Object.prototype.toString.call(object) == '[object Arguments]';
}

function regExpEquiv (a, b) {
    return a.source === b.source && a.global === b.global &&
           a.ignoreCase === b.ignoreCase && a.multiline === b.multiline;
}

function keys (obj) {
    if (Object.keys) {
        return Object.keys(obj);
    }

    var keys = [];

    for (var i in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, i)) {
            keys.push(i);
        }
    }

    return keys;
}

function ownProps(obj)
{
	return Object.getOwnPropertyNames(obj);
}


// return if array subSet is fully contained in array theSet
function isSubset(theSet, subSet) {
	for (var i = 0; i < subSet.length; i++) {
		if ( !~Array.prototype.indexOf.call(theSet, subSet[i]) ) return false;
				// note we use the tilde here since indexOf can return 0 if element is
				// the 1st one in the array!
	}
	return true;
}

function bindContext(theFunc, toContext) {
    return function() {
        theFunc.apply(toContext, arguments);
		return toContext;		// ensure that the calling method, gets its context back !!
    };
}

// get the name as a string, of the class of an object, ie. it's constructor
// Note that obj.constructor.name would work equally well, but some browsers don't support
// although this is not run in a browser, but usefull to keep in case we need it one day!
//
var getObjectClass = function (obj) {
    if (obj && obj.constructor && obj.constructor.toString()) {

        /*
         *  for browsers which have name property in the constructor
         *  of the object,such as chrome 
         */
        if(obj.constructor.name) {
            return obj.constructor.name;
        }
        var str = obj.constructor.toString();
        /*
         * executed if the return of object.constructor.toString() is 
         * "[object objectClass]"
         */

        if(str.charAt(0) == '[')
        {
                var arr = str.match(/\[\w+\s*(\w+)\]/);
        } else {
                /*
                 * executed if the return of object.constructor.toString() is 
                 * "function objectClass () {}"
                 * for IE Firefox
                 */
                var arr = str.match(/function\s*(\w+)/);
        }
        if (arr && arr.length == 2) {
                    return arr[1];
                }
  }
  return undefined; 
};

// script loading function
function jsLoadScript(f) { 
	eval(read(f));
};

function prettyPrintSHELL(obj){
    var toString = Object.prototype.toString,
        newLine = "\n", space = " ", tab = 8,
        buffer = "",        
        //Second argument is indent
        indent = arguments[1] || 0,
        //For better performance, Cache indentStr for a given indent.
        indentStr = (function(n){
            var str = "";
            while(n--){
                str += space;
            }
            return str;
        })(indent); 
 
    if(!obj || ( typeof obj != "object" && typeof obj!= "function" )){
        //any non-object ( Boolean, String, Number), null, undefined, NaN
        buffer += obj;
    }else if(toString.call(obj) == "[object Date]"){
        buffer += "[Date] " + obj;
    }else if(toString.call(obj) == "[object RegExp"){
        buffer += "[RegExp] " + obj;
    }else if(toString.call(obj) == "[object Function]"){
        buffer += "[Function] " + obj;
    }else if(toString.call(obj) == "[object Array]"){
        var idx = 0, len = obj.length;
        buffer += "["+newLine;
        while(idx < len){
            buffer += [
                indentStr, idx, ": ", 
                prettyPrintSHELL(obj[idx], indent + tab)
            ].join("");
            buffer += newLine;
            idx++;
        }
        buffer += indentStr + "]";
    }else { //Handle Object
        var prop;
        buffer += "{"+newLine;
        for(prop in obj){
            buffer += [
                indentStr, prop, ": ", 
                prettyPrintSHELL(obj[prop], indent + tab)
            ].join("");
            buffer += newLine;
        }
        buffer += indentStr + "}";
    }
 
    return buffer;
}

// test framework 1:
function tf() {};

( function() {
    var result;
    
    tf.assert = function(value, desc) {
        var assertion = value ? 'pass' : 'fail';
        result.push(['\n', '  ', desc, ': ', assertion].join(''));
        if (!value) result[1] = 'fail';
    }

    tf.test = function(name, testSuite) {
        result = [];
        result[0] = ['Test Suite: [', name, ']: '].join('');
        result[1] = 'pass';
        testSuite();
        print(result.join(''));
    }
    
} )();

// test harness 2
function performTestSuite( description, testSuite) {
	console.log('Performing test(s) for: ', description);
	testSuite();
}

function testFor(description, test) {
	var result = ['    Testing: ', description, ': '].join(' ');
	try {
		test();
		result = [result, 'SUCCESS'].join('');

	} catch(e) {
		result = [result, 'FAILED \n', '        ', e.message].join(' ');
	}
	console.log(result);
}



// unused for now
/**
function prettyPrintSHELLtest(obj){

    switch (type(obj))
    {
      case 'object':
        x="Today it's Saturday";
        break;
      case 'array':
        x="Today it's Sunday";
        break;
      case 'function':
      
        break;
      default:
        x="Looking forward to the Weekend";
    } 

}
**/

// Functions to print out vectors in the form of a url, for use in the vector visualiser
// http://www.bodurov.com/VectorVisualizer
//
function printTriangleShape(p, q, r) {
	return (
		printVector(p, q) +
		printVector(q, r) +
		printVector(r, p)
	);
};

function printVector(a,b) {
	// print('(',a,':',b,')');
	return ( a[0]+'/'+a[1]+'/'+a[2]+'/'+b[0]+'/'+b[1]+'/'+b[2]+'v' )
}

function outputTestConfigURL(p1,q1,r1, p2,q2,r2, a,b,c,d) {
	return (
		'http://www.bodurov.com/VectorVisualizer/?vectors=' +
		printTriangleShape(p1, q1, r1) +
		printTriangleShape(p2, q2, r2)
		//printVector(a,d) +
		//printVector(b,d) +
		//printVector(c,d) +
		//printVector( cross( minus(d,b), minus(d,c) ), d )
	);
};

function outputTriangleConfigURL(p1, q1, r1) {
	return (
		'http://www.bodurov.com/VectorVisualizer/?vectors=' +
		printTriangleShape(p1, q1, r1)
	);
}
